#include <iostream>
#include <fstream>
#include <vector>
#include <memory>
#include <algorithm>
#include "Customer.hh"
#include "CheckingAccount.hh"
#include "SavingsAccount.hh"
#include "Utilities.hh"

class BankingSystem {
private:
    std::vector<Customer*> customers;

    Customer* findCustomerByName(const std::string& name) {
        auto it = std::find_if(customers.begin(), customers.end(),
            [&name](Customer* c) { return c->getName() == name; });
        return (it != customers.end()) ? *it : nullptr;
    }

    void displayMainMenu() const {
        Utilities::clearScreen();
        std::cout << "===== Banking System =====\n";
        std::cout << "1. Register new customer\n";
        std::cout << "2. Access customer accounts\n";
        std::cout << "3. Display all bank accounts\n";
        std::cout << "4. Transaction reports\n";
        std::cout << "5. Exit\n";
        std::cout << "Please choose: ";
    }

    void registerNewCustomer() {
        std::string name;
        std::cout << "\n===== Register New Customer =====\n";
        std::cout << "Enter customer name: ";
        std::getline(std::cin, name);

        // Check if customer already exists
        if (findCustomerByName(name)) {
            std::cout << "Error: Customer with this name already exists.\n";
            Utilities::pressEnterToContinue();
            return;
        }

        // Create new customer
        Customer* newCustomer = new Customer(name);
        customers.push_back(newCustomer);

        // Create accounts for the customer
        std::string savingsId = Account::generateAccountId("Savings");
        std::string checkingId = Account::generateAccountId("Checking");
        
        SavingsAccount* savings = new SavingsAccount(savingsId, newCustomer, 0.0);
        CheckingAccount* checking = new CheckingAccount(checkingId, newCustomer, 0.0);

        std::cout << "\nCustomer " << name << " successfully registered!\n";
        std::cout << "Two bank accounts have been created:\n";
        std::cout << "- Savings Account: " << savingsId << "\n";
        std::cout << "- Checking Account: " << checkingId << "\n";
        Utilities::pressEnterToContinue();
    }

    void accessCustomerAccounts() {
        std::string name;
        std::cout << "\n===== Access Customer Accounts =====\n";
        std::cout << "Enter customer name: ";
        std::getline(std::cin, name);

        Customer* customer = findCustomerByName(name);
        if (!customer) {
            std::cout << "Error: Customer not found.\n";
            Utilities::pressEnterToContinue();
            return;
        }

        bool running = true;
        while (running) {
            Utilities::clearScreen();
            customer->displayCustomerInfo();

            std::cout << "Options:\n";
            std::cout << "1. Access Savings Account\n";
            std::cout << "2. Access Checking Account\n";
            std::cout << "3. View all transactions for this customer\n";
            std::cout << "4. Generate customer report\n";
            std::cout << "5. Return to main menu\n";
            std::cout << "Please choose: ";

            int choice;
            std::cin >> choice;
            std::cin.ignore(); // Clear newline

            switch (choice) {
                case 1: {
                    Account* savings = nullptr;
                    for (auto acc : customer->getAccounts()) {
                        if (acc->getAccountType() == "Savings") {
                            savings = acc;
                            break;
                        }
                    }
                    if (savings) accessBankAccount(savings);
                    break;
                }
                case 2: {
                    Account* checking = nullptr;
                    for (auto acc : customer->getAccounts()) {
                        if (acc->getAccountType() == "Checking") {
                            checking = acc;
                            break;
                        }
                    }
                    if (checking) accessBankAccount(checking);
                    break;
                }
                case 3:
                    viewCustomerTransactions(customer);
                    break;
                case 4:
                    generateCustomerReport(customer);
                    break;
                case 5:
                    running = false;
                    break;
                default:
                    std::cout << "Invalid choice. Please try again.\n";
                    Utilities::pressEnterToContinue();
            }
        }
    }

    void accessBankAccount(Account* account) {
        if (!account) return;

        bool running = true;
        while (running) {
            Utilities::clearScreen();
            account->displayAccountInfo();

            std::cout << "Options:\n";
            if (account->getAccountType() == "Checking") {
                std::cout << "1. Deposit\n";
                std::cout << "2. Withdraw\n";
                std::cout << "3. Transfer\n";
            }
            std::cout << "4. View transaction history\n";
            std::cout << "5. Generate bank account report\n";
            std::cout << "6. Return to customer page\n";
            std::cout << "Please choose: ";

            int choice;
            std::cin >> choice;
            std::cin.ignore(); // Clear newline

            switch (choice) {
                case 1:
                    if (account->getAccountType() == "Checking") {
                        performDeposit(dynamic_cast<CheckingAccount*>(account));
                    } else {
                        std::cout << "Error: Deposits are not allowed on savings accounts.\n";
                    }
                    break;
                case 2:
                    if (account->getAccountType() == "Checking") {
                        performWithdrawal(dynamic_cast<CheckingAccount*>(account));
                    } else {
                        std::cout << "Error: Withdrawals are not allowed on savings accounts.\n";
                    }
                    break;
                case 3:
                    performTransfer(dynamic_cast<CheckingAccount*>(account));
                    break;
                case 4:
                    account->displayTransactionHistory();
                    Utilities::pressEnterToContinue();
                    break;
                case 5:
                    generateAccountReport(account);
                    break;
                case 6:
                    running = false;
                    break;
                default:
                    std::cout << "Invalid choice. Please try again.\n";
                    Utilities::pressEnterToContinue();
            }
        }
    }

    void performDeposit(CheckingAccount* account) {
        if (!account) return;

        Utilities::clearScreen();
        std::cout << "\n===== Deposit to Account: " << account->getAccountId() << " =====\n";
        std::cout << "Current Balance: " << Utilities::formatCurrency(account->getBalance()) << "\n";
        std::cout << "Enter amount to deposit: ";
        
        double amount;
        std::cin >> amount;
        std::cin.ignore(); // Clear newline

        if (amount <= 0) {
            std::cout << "Error: Deposit amount must be positive.\n";
        } else {
            std::cout << "Add note (optional): ";
            std::string note;
            std::getline(std::cin, note);
            account->deposit(amount, note);
        }
        Utilities::pressEnterToContinue();
    }

    void performWithdrawal(CheckingAccount* account) {
        if (!account) return;

        Utilities::clearScreen();
        std::cout << "\n===== Withdraw from Account: " << account->getAccountId() << " =====\n";
        std::cout << "Current Balance: " << Utilities::formatCurrency(account->getBalance()) << "\n";
        std::cout << "Enter amount to withdraw: ";
        
        double amount;
        std::cin >> amount;
        std::cin.ignore(); // Clear newline

        if (amount <= 0) {
            std::cout << "Error: Withdrawal amount must be positive.\n";
        } else {
            std::cout << "Add note (optional): ";
            std::string note;
            std::getline(std::cin, note);
            account->withdraw(amount, note);
        }
        Utilities::pressEnterToContinue();
    }

    void performTransfer(CheckingAccount* account) {
        if (!account) return;

        Utilities::clearScreen();
        std::cout << "\n===== Transfer from Account: " << account->getAccountId() << " =====\n";
        std::cout << "Current Balance: " << Utilities::formatCurrency(account->getBalance()) << "\n";
        std::cout << "Enter destination account ID: ";
        
        std::string destId;
        std::getline(std::cin, destId);

        // Find destination account
        Account* destination = nullptr;
        for (auto customer : customers) {
            for (auto acc : customer->getAccounts()) {
                if (acc->getAccountId() == destId) {
                    destination = acc;
                    break;
                }
            }
            if (destination) break;
        }

        if (!destination) {
            std::cout << "Error: Destination account not found.\n";
            Utilities::pressEnterToContinue();
            return;
        }

        std::cout << "Enter amount to transfer: ";
        double amount;
        std::cin >> amount;
        std::cin.ignore(); // Clear newline

        if (amount <= 0) {
            std::cout << "Error: Transfer amount must be positive.\n";
        } else {
            std::cout << "Add note (optional): ";
            std::string note;
            std::getline(std::cin, note);
            account->transfer(destination, amount, note);
        }
        Utilities::pressEnterToContinue();
    }

    void viewCustomerTransactions(Customer* customer) {
        if (!customer) return;

        Utilities::clearScreen();
        std::cout << "\n===== Transaction History for Customer: " << customer->getName() << " =====\n";
        
        for (auto acc : customer->getAccounts()) {
            std::cout << "\nTransactions for Account: " << acc->getAccountId() << "\n";
            acc->displayTransactionHistory();
        }
        
        Utilities::pressEnterToContinue();
    }

    void generateCustomerReport(Customer* customer) {
        if (!customer) return;

        std::string filename = "transactions_" + customer->getName() + "_" + Utilities::getCurrentDateString() + ".txt";
        customer->generateCustomerReport(filename);
        std::cout << "Report successfully generated as \"" << filename << "\"\n";
        Utilities::pressEnterToContinue();
    }

    void generateAccountReport(Account* account) {
        if (!account) return;

        std::string filename = "transactions_" + account->getAccountId() + "_" + Utilities::getCurrentDateString() + ".txt";
        account->generateAccountReport(filename);
        std::cout << "Report successfully generated as \"" << filename << "\"\n";
        Utilities::pressEnterToContinue();
    }

    void displayAllBankAccounts() {
        Utilities::clearScreen();
        std::cout << "\n===== All Bank Accounts =====\n\n";

        // Group accounts by type
        std::vector<Account*> savingsAccounts;
        std::vector<Account*> checkingAccounts;
        double totalBalance = 0.0;

        for (auto customer : customers) {
            for (auto acc : customer->getAccounts()) {
                if (acc->getAccountType() == "Savings") {
                    savingsAccounts.push_back(acc);
                } else {
                    checkingAccounts.push_back(acc);
                }
                totalBalance += acc->getBalance();
            }
        }

        std::cout << "SAVINGS ACCOUNTS:\n";
        for (size_t i = 0; i < savingsAccounts.size(); ++i) {
            std::cout << (i + 1) << ". ID: " << savingsAccounts[i]->getAccountId() 
                      << "  |  Owner: " << savingsAccounts[i]->getAccountHolder()->getName()
                      << "  |  Balance: " << Utilities::formatCurrency(savingsAccounts[i]->getBalance()) << "\n";
        }

        std::cout << "\nCHECKING ACCOUNTS:\n";
        for (size_t i = 0; i < checkingAccounts.size(); ++i) {
            std::cout << (i + 1) << ". ID: " << checkingAccounts[i]->getAccountId() 
                      << "  |  Owner: " << checkingAccounts[i]->getAccountHolder()->getName()
                      << "  |  Balance: " << Utilities::formatCurrency(checkingAccounts[i]->getBalance()) << "\n";
        }

        std::cout << "\nTotal Accounts: " << (savingsAccounts.size() + checkingAccounts.size()) << "\n";
        std::cout << "Total Customers: " << customers.size() << "\n";
        std::cout << "Total Balance: " << Utilities::formatCurrency(totalBalance) << "\n";
        Utilities::pressEnterToContinue();
    }

    void generateGlobalTransactionReport() {
        std::string filename = "transactions_" + Utilities::getCurrentDateString() + ".txt";
        std::ofstream outFile(filename);
        if (!outFile) {
            std::cerr << "Error: Unable to create report file.\n";
            Utilities::pressEnterToContinue();
            return;
        }

        outFile << "===== Global Transaction Report: " << Utilities::getCurrentDateString() << " =====\n\n";
        
        // Collect all transactions from all accounts
        std::vector<Transaction> allTransactions;
        for (auto customer : customers) {
            for (auto acc : customer->getAccounts()) {
                for (auto t : acc->getTransactions()) {
                    allTransactions.push_back(t);
                }
            }
        }

        // Sort transactions by timestamp
        std::sort(allTransactions.begin(), allTransactions.end(),
            [](const Transaction& a, const Transaction& b) {
                return a.getTimestamp() < b.getTimestamp();
            });

        // Write transactions to file
        for (const auto& t : allTransactions) {
            outFile << t << "\n";
        }

        outFile.close();
        std::cout << "Report successfully generated as \"" << filename << "\"\n";
        Utilities::pressEnterToContinue();
    }

public:
    ~BankingSystem() {
        for (auto customer : customers) {
            delete customer;
        }
    }

    void run() {
        bool running = true;
        while (running) {
            displayMainMenu();

            int choice;
            std::cin >> choice;
            std::cin.ignore(); // Clear newline

            switch (choice) {
                case 1:
                    registerNewCustomer();
                    break;
                case 2:
                    accessCustomerAccounts();
                    break;
                case 3:
                    displayAllBankAccounts();
                    break;
                case 4: {
                    std::cout << "\n===== Transaction Reports =====\n";
                    std::cout << "1. Generate global transaction report\n";
                    std::cout << "2. Return to main menu\n";
                    std::cout << "Please choose: ";
                    
                    int reportChoice;
                    std::cin >> reportChoice;
                    std::cin.ignore();
                    
                    if (reportChoice == 1) {
                        generateGlobalTransactionReport();
                    }
                    break;
                }
                case 5:
                    running = false;
                    break;
                default:
                    std::cout << "Invalid choice. Please try again.\n";
                    Utilities::pressEnterToContinue();
            }
        }
    }
};

int main() {
    BankingSystem system;
    system.run();
    return 0;
}